/*
 * Copyright (c) 2013, SRI International
 * All rights reserved.
 * Licensed under the The BSD 3-Clause License;
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://opensource.org/licenses/BSD-3-Clause
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the aic-web-praise nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.sri.ai.lpitools.gwtgui.client.components;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.widgetideas.client.ProgressBar;
import com.sri.ai.lpitools.gwtgui.client.Toolkit;
import com.sri.ai.lpitools.gwtgui.client.components.editor.ModelEditor;
import com.sri.ai.lpitools.gwtgui.client.components.graphpanel.ResultModel;
import com.sri.ai.lpitools.gwtgui.client.components.graphpanel.SRIGraphPanel;
import com.sri.ai.lpitools.gwtgui.client.components.graphpanel.SRIGraphTable;
import com.sri.ai.lpitools.gwtgui.client.components.manager.ExecutionEngineListener;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.EvidencePanel;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.QueryOptionsPanel;
import com.sri.ai.lpitools.gwtgui.client.components.querypanel.QueryPanel;
import com.sri.ai.lpitools.gwtgui.client.components.treepanels.JustificationTreePanel;
import com.sri.ai.lpitools.gwtgui.client.components.treepanels.TraceTreePanel;
import com.sri.ai.lpitools.gwtgui.client.util.JSONUtil;
import com.sri.ai.lpitools.gwtgui.client.util.QueryResultTabListener;

public class MainView extends SplitLayoutPanel implements ExecutionEngineListener {

	private ProgressBar mainProgressBar;
	private JustificationTreePanel jPanel;
	private TraceTreePanel tPanel;
	private SRIGraphPanel graphPanel;
	private ModelEditor editor;
	private ScrollPanel showExpression;
	private TextArea resultText;
	private TabLayoutPanel queryTabLayout;
	private QueryPanel qp;
	private EvidencePanel ep;
	private Toolkit toolkit;
	private SRIGraphTable tablePanel;
	
	public MainView(Toolkit toolkit) {
		super(5);
		this.toolkit = toolkit;
		this.toolkit.getExecutionEngineDistributor().addListener(this);
		
		DockLayoutPanel leftDockPanel = new DockLayoutPanel(Unit.EM);

		editor = new ModelEditor(this.toolkit.getModelManager());
		// Set the mode of the editor to edit LPI Models.
		leftDockPanel.add(editor);
		
		Label queryLabel = new Label("Query: ");
		Label evidenceLabel = new Label("Evidence: ");
		queryLabel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
		evidenceLabel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
				
		mainProgressBar = new ProgressBar(0.0, 100, 0.0);
		mainProgressBar.setProgress(0);
		mainProgressBar.setWidth("100%");
				
		DockLayoutPanel queryPanel = new DockLayoutPanel(Unit.EM);
		
		TabLayoutPanel queryTabs = new TabLayoutPanel(24, Unit.PX);
		queryTabs.setSize("100%", "100%");
		
		qp = new QueryPanel(toolkit);
		ep = new EvidencePanel(toolkit);
		
		QueryOptionsPanel qop = new QueryOptionsPanel(toolkit.getQueryOptionsManager());

		queryTabs.add(qp, "Query");
		
		queryTabs.add(qop, "Options");
		queryTabs.add(ep, "Evidences");
		queryPanel.addNorth(queryTabs, 7);
		queryPanel.addNorth(mainProgressBar, 2);
		
		queryTabLayout = new TabLayoutPanel(24, Unit.PX);
				
		graphPanel = new SRIGraphPanel("Graphs");
		graphPanel.clear();
		graphPanel.clearData();
		
		tablePanel = new SRIGraphTable();
		tablePanel.clearData();
		//build result tab for the query tab panel

		SplitLayoutPanel queryResultsPanel = new SplitLayoutPanel();
		resultText = new TextArea();
		resultText.setSize("100%", "100%");
		resultText.setReadOnly(true);
		showExpression = new ScrollPanel();
		queryResultsPanel.addSouth(resultText, 60);
		queryResultsPanel.add(showExpression);
		
		//build tree panels
		jPanel = new JustificationTreePanel();
		tPanel = new TraceTreePanel();
		
		//build Performance area
		DockLayoutPanel performancePanel = new DockLayoutPanel(Unit.EM);
		performancePanel.add(graphPanel);
		DockLayoutPanel performancePanelTable = new DockLayoutPanel(Unit.EM);
		performancePanelTable.add(tablePanel);
		//add panels to queryTabLayout
		queryTabLayout.add(queryResultsPanel, "Result");
		queryTabLayout.add(jPanel, "Justification");
		queryTabLayout.add(tPanel, "Trace");	
		queryTabLayout.add(performancePanel, "Performance Graph");
		queryTabLayout.add(performancePanelTable, "Performance Table");
		
		queryTabLayout.selectTab(0);
		queryTabLayout.setSize("100%", "100%");
		queryTabLayout.addBeforeSelectionHandler(new QueryResultTabListener(this));
		queryPanel.add(queryTabLayout);
		
		addEast(queryPanel, 500);
		add(leftDockPanel);
	}
	
	@Override
	public void resultReceived(JSONObject jsv) {
		queryTabLayout.selectTab(0);
		graphPanel.clear();
		graphPanel.clearData();
		showExpression.clear();
		resultText.setText("");
		graphPanel.clear();
		graphPanel.clearData();
		tablePanel.clearData();
		String resultString= JSONUtil.getStringValue(jsv, "result");
		List<Double> values= cleanJSONArrayDouble((JSONArray) jsv.get("timeValues"));
		List<String> titles= cleanJSONArray((JSONArray) jsv.get("timeTitles"));
		List<ResultModel> list = processResults(titles, values);
		graphPanel.addRows(list);
		tablePanel.setListContent(titles, values);
		setProgress("done");
		graphPanel.addGraph();
		resultText.setText(resultString);
		toolkit.getAicManager().getLatexImage(resultString);
	}
	
	private List<ResultModel> processResults(List<String> titles, List<Double> values){
		List<ResultModel> list = new ArrayList<ResultModel>();
		for(int i = 0; i<titles.size(); i++){
			String name = titles.get(i);	
			double value = values.get(i);	
			ResultModel rModel = new ResultModel(name, value, value+"", i+1);
			list.add(rModel);
		}
		return list;
	}
	
	@Override
	public void imageTexLiveReceived(String result) {
		queryTabLayout.selectTab(0);
		showExpression.clear();	
		Image image = new Image(result);
		showExpression.setWidget(image);
	}	
		
	public void updateTime(String message){
		setProgress(message);
	}
	
	/**
	 * This function takes in a JSONArray and return an array of cleaned Strings
	 * @param jArray
	 * @return
	 */
	private List<Double> cleanJSONArrayDouble(JSONArray jArray){
		List<String> list = JSONUtil.cleanJSONArray(jArray);
		List<Double> values = new ArrayList<Double>();
		for(int i = 0; i<list.size(); i++){
			String valueS = list.get(i);
			long value = Long.parseLong(valueS);
			double valueSecond = value/1000.00;
			values.add(valueSecond);
		}
		return values;
	}
	/**
	 * This function takes in a JSONArray and return an array of cleaned Strings
	 * @param jArray
	 * @return
	 */
	private List<String> cleanJSONArray(JSONArray jArray){
		return JSONUtil.cleanJSONArray(jArray);
	}
	/**
	 * This function updates the progress bar for the query run
	 * @param progress
	 */
	private void setProgress(String progress){
		if(progress.equalsIgnoreCase("done")){
			mainProgressBar.setProgress(100);
		} else if(progress.equalsIgnoreCase("After Step 1")){
			mainProgressBar.setProgress(14.28);
		} else if(progress.equalsIgnoreCase("After Step 2")){
			mainProgressBar.setProgress(28.56);
		} else if(progress.equalsIgnoreCase("After Step 3")){
			mainProgressBar.setProgress(42.84);
		} else if(progress.equalsIgnoreCase("After Step 4")){
			mainProgressBar.setProgress(57.12);
		} else if(progress.equalsIgnoreCase("After Step 5")){
			mainProgressBar.setProgress(71.4);
		} else if(progress.equalsIgnoreCase("After Step 6")){
			mainProgressBar.setProgress(85.68);
		} else if(progress.equalsIgnoreCase("After Step 7")){
			mainProgressBar.setProgress(100.00);
		}
	}
	
	public void renderTrees() {
		jPanel.renderTree();
		tPanel.renderTree();
	}
	
	public void setClientID(String clientID) {
		jPanel.setClientID(clientID);
		tPanel.setClientID(clientID);
	}
	
	public void updateEditor(String currentModelText){
		editor.reFormatText(currentModelText);
	}
	
	@Override
	public void clientIDReceived(String clientID) {
		setClientID(clientID);
	}

	@Override
	public void messageReceived(String message) {
		updateTime(message);
	}
}
